#include <algorithm>
#include <pthread.h>
#include <unistd.h>
#include <logger.h>

#include "gather.h"
#include "iot_debug.h"
#include "dx_type.h"
#include "tools/auto_guard.h"
#include "tools/m_mutex.h"
#include "tools/m_sleep.h"
#include "phylink.h"
#include "idevice.h"
#include "physicallinkmanager.h"


void* gatherThreadProc(void *argv)
{
    Gather * gather = reinterpret_cast<Gather *>(argv);

    while (gather->started_) {
        gather->gatherProc();
        mSleep(gather->poll_time_ms_);
    }
    return argv;
}

Gather::Gather() :
    poll_cnt_(0),
    poll_time_ms_(10),
    started_(false)
{

}

Gather::~Gather()
{
    stopGather();
}

bool Gather::addDevice(IDevice *dev)
{
    AUTO_GUARD(gd, MMutex, mtx_);
    if (containof(dev, active_devices_))
        return false;

    if (containof(dev, inactive_devices_))
        return false;

    active_devices_.push_back(dev);
    return true;
}

void Gather::removeDevice(IDevice *dev)
{
    AUTO_GUARD(gd, MMutex, mtx_);
    if (containof(dev, active_devices_))
        active_devices_.remove(dev);

    if (containof(dev, inactive_devices_))
        inactive_devices_.remove(dev);
}

void Gather::startGather(void *v)
{
    if (started_)
        stopGather();

    if (active_devices_.size()) {
        IDevice * dev = *active_devices_.begin();
        const ComSettings * settings = PhysicalLinkManager::mgr()->getDevice(dev->bayId())->comSettings();
        if (settings) {
            this->poll_time_ms_ = settings->poll_call_wait_time_ms;
            this->retry_after_lost_ = settings->ignore_call_cnt_after_link_lost;
        }
        else {
            this->poll_time_ms_ = 100;
            this->retry_after_lost_ = 5;
        }
    }

    pthread_t tid;
    started_ = true;
    poll_cnt_ = 0;

    pthread_create(&tid, nullptr, gatherThreadProc, this);
}

void Gather::stopGather()
{
    started_ = false;
    mSleep(poll_time_ms_+1);
}

void Gather::gatherProc()
{
    AUTO_GUARD(gd, MMutex, this->mtx_);
    Gather::IDevices::iterator it,it_next;
    for (it = active_devices_.begin();
         it != active_devices_.end();
         it = it_next)
    {
        it_next = it;
        ++it_next;
        IDevice *dev = *it;
        if (!dev->enabled()) {
            continue;
        }

        int err = dev->updateReg();
        if (ERR_NO_ERROR != err) {
            log_info("dev:%s link lost(err_str:%s)\n", dev->devName().c_str(), DXErrorString(err));
            inactive_devices_.push_back(*it);
            active_devices_.erase(it);
        }
        else {}


        dev->actions();
    }
    poll_cnt_++;
    if (poll_cnt_ >= this->retry_after_lost_) 
    {
        poll_cnt_ = 0;

        if (active_devices_.size() == 0)
        {
            IDevice *dev = inactive_devices_.front();
            if (dev->getRelayProcotol() == "" &&
                (dev->getProcotol() == PRO_MODBUS))
            {
                dev->com()->restore();
            }
        }
        for (it = inactive_devices_.begin(); it != inactive_devices_.end(); it = it_next)
        {
            it_next = it;
            ++it_next;
            IDevice *dev = *it;
            if (!dev->enabled()) 
            {
                continue;
            }
            if (ERR_NO_ERROR == dev->updateReg()) 
            {
                log_info("devId:%u in bayId:%d link recover\n", dev->devId(), dev->bayId());
                active_devices_.push_back(dev);
                inactive_devices_.erase(it);
            }
            else {}
        }
    }
}

bool Gather::containof(IDevice *dev, const IDevices &devs)
{
    return std::find(devs.begin(), devs.end(), dev) != devs.end();
}
